home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / smailsrc.zip / SMAIL.ZIP / DELIVER.C < prev    next >
Text File  |  1990-05-05  |  14KB  |  551 lines

  1. /*
  2. **  Deliver.c
  3. **
  4. **  Routines to effect delivery of mail for rmail/smail. 
  5. **
  6. */
  7.  
  8. /*
  9. **  Patched for MS-DOS compatibility by Stephen Trier
  10. **  March, 1990     - Version 0.1 alpha
  11. **  Mid-April, 1990 - Version 0.1 beta
  12. **  May, 1990       - Version 1.0 beta
  13. */
  14.  
  15. #ifndef lint
  16. static char     *sccsid="@(#)deliver.c    2.5 (smail) 9/15/87";
  17. #endif
  18.  
  19. # include    <stdio.h>
  20. # include    <sys/types.h>
  21. # include    <sys/stat.h>
  22. # include    <ctype.h>
  23. # include    <signal.h>
  24. # include    "defs.h"
  25. #ifdef MSDOS
  26. # include       <process.h>
  27. # include       <fcntl.h>
  28. # include       <setjmp.h>
  29. #endif
  30.  
  31. extern int  exitstat;        /* set if a forked mailer fails */
  32. extern enum edebug debug;    /* how verbose we are         */ 
  33. extern char hostname[];        /* our uucp hostname         */
  34. extern char hostdomain[];    /* our host's domain         */
  35. extern enum ehandle handle;    /* what we handle        */
  36. extern enum erouting routing;    /* how we're routing addresses  */
  37. extern char *uuxargs;        /* arguments given to uux       */
  38. extern int  queuecost;        /* threshold for queueing mail  */
  39. extern int  maxnoqueue;        /* max number of uucico's       */
  40. extern char *spoolfile;        /* file name of spooled message */
  41. extern FILE *spoolfp;        /* file ptr  to spooled message */
  42. extern int spoolmaster;        /* set if creator of spoolfile  */
  43. extern char nows[];        /* local time in ctime(3) format*/
  44. extern char arpanows[];        /* local time in arpadate format*/
  45. #ifndef MSDOS
  46. char stderrfile[20];            /* error file for stderr traping*/
  47. #else
  48. char stderrfile[80];            /* error file for stderr traping*/
  49. #endif
  50.  
  51. /*
  52. **
  53. **  deliver():  hand the letter to the proper mail programs.
  54. **
  55. **  Issues one command for each different host of <hostv>,
  56. **  constructing the proper command for LOCAL or UUCP mail.
  57. **  Note that LOCAL mail has blank host names.
  58. **
  59. **  The <userv> names for each host are arguments to the command.
  60. ** 
  61. **  Prepends a "From" line to the letter just before going 
  62. **  out, with a "remote from <hostname>" if it is a UUCP letter.
  63. **
  64. */
  65.  
  66. deliver(argc, hostv, userv, formv, costv)
  67. int argc;                /* number of addresses        */
  68. char *hostv[];                /* host names            */
  69. char *userv[];                /* user names            */
  70. enum eform formv[];            /* form for each address    */
  71. int costv[];                /* cost vector             */
  72. {
  73.     FILE *out;            /* pipe to mailer        */
  74.     FILE *popen();            /* to fork a mailer         */
  75. #ifdef RECORD
  76.     void record();            /* record all transactions    */
  77. #endif
  78. #ifdef LOG
  79.     void log();
  80. #endif
  81.     char *mktemp();
  82.     char from[SMLBUF];        /* accumulated from argument     */
  83.     char lcommand[SMLBUF];        /* local command issued     */
  84.     char rcommand[SMLBUF];        /* remote command issued    */
  85.     char scommand[SMLBUF];        /* retry  command issued    */
  86.     char *command;            /* actual command        */
  87.     char buf[SMLBUF];        /* copying rest of the letter   */
  88.     enum eform form;        /* holds form[i] for speed     */
  89.     long size;            /* number of bytes of message     */
  90.     char *flags;            /* flags for uux        */
  91.     char *sflag;            /* flag  for smail        */
  92.     int i, j, status, retrying;
  93.     char *c, *postmaster();
  94. #ifndef MSDOS
  95.     int failcount = 0;
  96. #else
  97.     int k, stderrfd;
  98. #endif
  99.     int noqcnt = 0;            /* number of uucico's started   */
  100.     char *uux_noqueue = UUX_NOQUEUE;/* uucico starts immediately    */
  101.     char *uux_queue   = UUX_QUEUE;    /* uucico job gets queued       */
  102.     off_t message;
  103.     struct stat st;
  104.  
  105. /*
  106. ** rewind the spool file and read the collapsed From_ line
  107. */
  108.     (void) fseek(spoolfp, 0L, 0);
  109.     (void) fgets(from, sizeof(from), spoolfp);
  110.     if((c = index(from, '\n')) != 0) *c = '\0';
  111.     message = ftell(spoolfp);
  112.  
  113. /*
  114. **  We pass through the list of addresses.
  115. */
  116.     stderrfile[0] = '\0';
  117.     for(i = 0; i < argc; i++) {
  118.         char *lend = lcommand;
  119.         char *rend = rcommand;
  120.         char *send = scommand;
  121.  
  122. /*
  123. **  If we don't have sendmail, arrange to trap standard error
  124. **  for inclusion in the message that is returned with failed mail.
  125. */
  126.         (void) unlink(stderrfile);
  127. #ifndef MSDOS
  128.         (void) strcpy(stderrfile, "/tmp/stderrXXXXXX");
  129.         (void) mktemp(stderrfile);
  130.         (void) freopen(stderrfile, "w", stderr);
  131.         if(debug != YES) {
  132.             (void) freopen(stderrfile, "w", stdout);
  133.         }
  134. #else /* MSDOS */
  135.         (void) sprintf(stderrfile, "%s/stderrXXXXXX", ms_tmpdir);
  136.         (void) mktemp(stderrfile);
  137.         stderrfd = open(stderrfile, O_CREAT|O_APPEND, S_IREAD|S_IWRITE);
  138.         dup2(stderrfd, 2);
  139.         if (debug != NO)
  140.             dup2(stderrfd, 2);
  141. #endif /* !MSDOS */
  142.  
  143.         *lend = *rend = *send = '\0';
  144.  
  145. /*
  146. **  If form == ERROR, the address was bad 
  147. **  If form == SENT, it has been sent on a  previous pass.
  148. */
  149.         form = formv[i];
  150.         if (form == SENT) {
  151.             continue;
  152.         }
  153. /*
  154. **  Build the command based on whether this is local mail or uucp mail.
  155. **  By default, don't allow more than 'maxnoqueue' uucico commands to
  156. **  be started by a single invocation of 'smail'.
  157. */
  158.         if(uuxargs == NULL) {    /* flags not set on command line */
  159.             if(noqcnt < maxnoqueue && costv[i] <= queuecost) {
  160.                 flags = uux_noqueue;
  161.             } else {
  162.                 flags = uux_queue;
  163.             }
  164.         } else {
  165.             flags = uuxargs;
  166.         }
  167.  
  168.         retrying = 0;
  169.         if(routing == JUSTDOMAIN) {
  170.             sflag = "-r";
  171.         } else if(routing == ALWAYS) {
  172.             sflag = "-R";
  173.         } else {
  174.             sflag = "";
  175.         }
  176.  
  177.         (void) sprintf(lcommand, LMAIL(from, hostv[i]));
  178.         (void) sprintf(rcommand, RMAIL(flags, from, hostv[i]));
  179.  
  180. /*
  181. **  For each address with the same host name and form, append the user
  182. **  name to the command line, and set form = ERROR so we skip this address
  183. **  on later passes. 
  184. */
  185.         /* we initialized lend (rend) to point at the
  186.          * beginning of its buffer, so that at
  187.          * least one address will be used regardless
  188.          * of the length of lcommand (rcommand).
  189.          */
  190.         for (j = i; j < argc; j++) {
  191.             if ((formv[j] != form)
  192.              || (strcmpic(hostv[i], hostv[j]) != 0)
  193.              || ((lend - lcommand) > MAXCLEN)
  194.              || ((rend - rcommand) > MAXCLEN)) {
  195.                 continue;
  196.             }
  197.  
  198.             /*
  199.             ** seek to the end of scommand
  200.             ** and add on a 'smail' command
  201.             ** multiple commands are separated by ';'
  202.             */
  203.  
  204.             send += strlen(send);
  205.             if(send != scommand) {
  206.                 *send++ = ';' ;
  207.             }
  208.  
  209.             (void) sprintf(send, RETRY(sflag));
  210.             send += strlen(send);
  211.  
  212.             lend += strlen(lend);
  213.             rend += strlen(rend);
  214.  
  215.             if (form == LOCAL) {
  216.                 (void) sprintf(lend, LARG(userv[j]));
  217.                 (void) sprintf(send, LARG(userv[j]));
  218.             } else {
  219.                 (void) sprintf(lend, RLARG(hostv[i], userv[j]));
  220.                 (void) sprintf(send, RLARG(hostv[i], userv[j]));
  221.             }
  222.  
  223.             (void) sprintf(rend, RARG(userv[j]));
  224.             formv[j] = SENT;
  225.         }
  226. retry:
  227. /*
  228. ** rewind the spool file and read the collapsed From_ line
  229. */
  230.         (void) fseek(spoolfp, message, 0);
  231.  
  232.         /* if the address was in a bogus form (usually DOMAIN),
  233.         ** then don't bother trying the uux.
  234.         **
  235.         ** Rather, go straight to the next smail routing level.
  236.         */
  237.         if(form == ERROR) {
  238.             static char errbuf[SMLBUF];
  239.             (void) sprintf(errbuf,
  240.                 "address resolution ('%s' @ '%s') failed",
  241.                     userv[i], hostv[i]);
  242.             command = errbuf;
  243.             size    = 0;
  244.             goto form_error;
  245.         }
  246.  
  247.         if (retrying) {
  248.             command = scommand;
  249.         } else if (form == LOCAL) {
  250.             command = lcommand;
  251.         } else {
  252.             command = rcommand;
  253.             if(flags == uux_noqueue) {
  254.                 noqcnt++;
  255.             }
  256.         }
  257.         ADVISE("COMMAND: %s\n", command);
  258.  
  259. /*
  260. ** Fork the mailer and set it up for writing so we can send the mail to it,
  261. ** or for debugging divert the output to stdout.
  262. */
  263.  
  264. /*
  265. ** We may try to write on a broken pipe, if the uux'd host
  266. ** is unknown to us.  Ignore this signal, since we can use the
  267. ** return value of the pclose() as our indication of failure.
  268. */
  269. #ifndef MSDOS
  270.         (void) signal(SIGPIPE, SIG_IGN);
  271. #endif
  272.  
  273.         if (debug == YES) {
  274.             out = stdout;
  275.         } else {
  276. #ifndef MSDOS
  277.             failcount = 0;
  278.             do {
  279.                 out = popen(command, "w");
  280.                 if (out) break;
  281.                 /*
  282.                  * Fork failed.  System probably overloaded.
  283.                  * Wait awhile and try again 10 times.
  284.                  * If it keeps failing, probably some
  285.                  * other problem, like no uux or smail.
  286.                  */
  287.                 (void) sleep(60);
  288.             } while (++failcount < 10);
  289. #else  /* MSDOS */
  290.             out = popen(command, "w");
  291.             /*
  292.              * A failed fork under MS-DOS is just that.  Don't
  293.              * bother retrying it; it won't get any better.
  294.              */
  295. #endif  /* !MSDOS */
  296.         }
  297.         if(out == NULL) {
  298.             exitstat = EX_UNAVAILABLE;
  299.             (void) printf("couldn't execute %s.\n", command);
  300.             continue;
  301.         }
  302.  
  303.         size = 0;
  304.         if(fstat(fileno(spoolfp), &st) >= 0) {
  305.             size = st.st_size - message;
  306.         }
  307. /*
  308. **  Output our From_ line.
  309. */
  310.         if (form == LOCAL) {
  311. #ifdef SENDMAIL
  312.             (void) sprintf(buf, LFROM(from, nows, hostname));
  313.             size += strlen(buf);
  314.             (void) fputs(buf, out);
  315. #else
  316.             char *p;
  317.             if((p=index(from, '!')) == NULL) {
  318.                 (void) sprintf(buf, LFROM(from, nows, hostname));
  319.                 size += strlen(buf);
  320.                 (void) fputs(buf, out);
  321.             } else {
  322.                 *p = NULL;
  323.                 (void) sprintf(buf, RFROM(p+1, nows, from));
  324.                 size += strlen(buf);
  325.                 (void) fputs(buf, out);
  326.                 *p = '!';
  327.             }
  328. #endif
  329.         } else {
  330.             (void) sprintf(buf, RFROM(from, nows, hostname));
  331.             size += strlen(buf);
  332.             (void) fputs(buf, out);
  333.         }
  334.  
  335. #ifdef SENDMAIL
  336. /*
  337. **  If using sendmail, insert a Received: line only for mail
  338. **  that is being passed to uux.  If not using sendmail, always
  339. **  insert the received line, since sendmail isn't there to do it.
  340. */
  341.         if(command == rcommand && handle != ALL)
  342. #endif
  343.         {
  344.             (void) sprintf(buf,
  345.                 "Received: by %s (%s)\n\tid AA%05d; %s\n",
  346.                     hostdomain, VERSION,
  347.                     getpid(), arpanows);
  348.             size += strlen(buf);
  349.             (void) fputs(buf, out);
  350.         }
  351.  
  352. /*
  353. **  Copy input.
  354. */
  355.         while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
  356.             (void) fputs(buf, out);
  357.         }
  358. /*
  359. **  Get exit status and if non-zero, set global exitstat so when we exit
  360. **  we can indicate an error.
  361. */
  362. form_error:
  363.         if (debug != YES) {
  364.             if(form == ERROR) {
  365.                 exitstat = EX_NOHOST;
  366.             } else if (status = pclose(out)) {
  367.                 exitstat = status >> 8;
  368.             }
  369.             /*
  370.              * The 'retrying' check prevents a smail loop.
  371.              */
  372.             if(exitstat != 0) {
  373.                 /*
  374.                 ** the mail failed, probably because the host
  375.                 ** being uux'ed isn't in L.sys or local user
  376.                 ** is unknown.
  377.                 */
  378.  
  379.                 if((retrying == 0)    /* first pass */
  380.                 && (routing != REROUTE)    /* have higher level */
  381.                 && (form != LOCAL)) {    /* can't route local */
  382.                     /*
  383.                     ** Try again using a higher
  384.                     ** level of routing.
  385.                     */
  386.                     ADVISE("%s failed (%d)\ntrying %s\n",
  387.                         command, exitstat, scommand);
  388.                     exitstat = 0;
  389.                     retrying = 1;
  390.                     form = SENT;
  391. #ifndef MSDOS
  392.                     goto retry;
  393. #else
  394.                     for (k = i; k < argc; k++) {
  395.                         if ((formv[k] != form)
  396.                          || (strcmpic(hostv[i], hostv[k]) != 0)
  397.                          || ((lend - lcommand) > MAXCLEN)
  398.                          || ((rend - rcommand) > MAXCLEN)) {
  399.                         continue;
  400.                         }
  401.                         formv[k] = ROUTE; /* Try again */
  402.                     }
  403. #endif
  404.                 }
  405.  
  406.                 /*
  407.                 ** if we have no other routing possibilities
  408.                 ** see that the mail is returned to sender.
  409.                 */
  410.  
  411.                 if((routing == REROUTE)
  412.                     || (form == LOCAL)) {
  413.  
  414.                     /*
  415.                     ** if this was our last chance,
  416.                     ** return the mail to the sender.
  417.                     */
  418.  
  419.                     ADVISE("%s failed (%d)\n",
  420.                         command, exitstat);
  421.                     
  422.                     (void) fseek(spoolfp, message, 0);
  423. #ifdef SENDMAIL
  424.                     /* if we have sendmail, then it
  425.                     ** was handed the mail, which failed.
  426.                     ** sendmail returns the failed mail
  427.                     ** for us, so we need not do it again.
  428.                     */
  429.                     if(form != LOCAL)
  430. #endif
  431.                     {
  432.                         return_mail(from, command);
  433.                     }
  434.                     exitstat = 0;
  435.                 }
  436.             }
  437. # ifdef LOG
  438.             else {
  439.                 if(retrying == 0) log(command, from, size); /* */
  440.             }
  441. # endif
  442.         }
  443.     }
  444. /*
  445. **  Update logs and records.
  446. */
  447. # ifdef RECORD
  448.     (void) fseek(spoolfp, message, 0);
  449.     record(command, from, size);
  450. # endif
  451.  
  452. /*
  453. **  close spool file pointer.
  454. **  if we created it, then unlink file.
  455. */
  456. #ifndef MSDOS
  457.     (void) fclose(spoolfp);
  458.     if(spoolmaster) {
  459.         (void) unlink(spoolfile);
  460.     }
  461. #else /* MSDOS */
  462.     /*
  463.      * Close and unlink the spool file in the main program; we might need
  464.      * it again.
  465.      */
  466.     close(stderrfd);
  467.     fclose(stderr);
  468. #endif /* !MSDOS */
  469.     (void) unlink(stderrfile);
  470. }
  471.  
  472. /*
  473. ** return mail to sender, as determined by From_ line.
  474. */
  475. return_mail(from, fcommand)
  476. char *from, *fcommand;
  477. {
  478.     char buf[SMLBUF];
  479.     char domain[SMLBUF], user[SMLBUF];
  480.     char *r;
  481.     FILE *fp, *out, *popen();
  482. #ifndef MSDOS
  483.     int i = 0;
  484. #endif
  485.  
  486.     r = buf;
  487.  
  488.     (void) sprintf(r, "%s %s", SMAIL, VFLAG);
  489.     r += strlen(r);
  490.  
  491.     if(islocal(from, domain, user)) {
  492.         (void) sprintf(r, LARG(user));
  493.     } else {
  494.         (void) sprintf(r, RLARG(domain, user));
  495.     }
  496.  
  497. #ifndef MSDOS
  498.     i = 0;
  499.     do {
  500.         out = popen(buf, "w");
  501.         if (out) break;
  502.         /*
  503.          * Fork failed.  System probably overloaded.
  504.          * Wait awhile and try again 10 times.
  505.          * If it keeps failing, probably some
  506.          * other problem, like no uux or smail.
  507.          */
  508.         (void) sleep(60);
  509.     } while (++i < 10);
  510. #else /* !MSDOS */
  511.     out = popen(buf, "w");
  512.     /*
  513.      * Don't bother retrying it if the fork failed. After
  514.      * all, it's awfully hard for an MS-DOS machine to be
  515.      * running too many processes at once...  :-)
  516.      */
  517. #endif
  518.  
  519.     if(out == NULL) {
  520.         (void) printf("couldn't execute %s.\n", buf);
  521.         return;
  522.     }
  523.  
  524.     (void) fprintf(out, "Date: %s\n", arpanows);
  525.     (void) fprintf(out, "From: MAILER-DAEMON@%s\n", hostdomain);
  526.     (void) fprintf(out, "Subject: failed mail\n");
  527.     (void) fprintf(out, "To: %s\n", from);
  528.     (void) fprintf(out, "\n");
  529.     (void) fprintf(out, "=======     command failed      =======\n\n");
  530.     (void) fprintf(out, " COMMAND: %s\n\n", fcommand);
  531.  
  532.     (void) fprintf(out, "======= standard error follows  =======\n");
  533.     (void) fflush(stderr);
  534.     if((fp = fopen(stderrfile, "r")) != NULL) {
  535.         while(fgets(buf, sizeof(buf), fp) != NULL) {
  536.             (void) fputs(buf, out);
  537.         }
  538.     }
  539.     (void) fclose(fp);
  540.     (void) fprintf(out, "\n");
  541.     (void) fprintf(out, "======= text of message follows =======\n");
  542. /*
  543. **  Copy input.
  544. */
  545.     (void) fprintf(out, "From %s\n", from);
  546.     while(fgets(buf, sizeof(buf), spoolfp) != NULL) {
  547.         (void) fputs(buf, out);
  548.     }
  549.     (void) pclose(out);
  550. }
  551.